package mymyoux.io.mserveur
{
	import flash.net.registerClassAlias;
	import flash.utils.setTimeout;
	
	import mx.controls.Alert;
	
	import mymyoux.events.AMFPHPEvent;
	import mymyoux.events.MEventDispatcher;
	import mymyoux.io.mserveur.events.MRoomEvent;
	import mymyoux.io.mserveur.events.MBinarySocketEvent;
	import mymyoux.io.AMFPHP;
	import mymyoux.io.mserveur.mapping.PyGram;
	import mymyoux.io.mserveur.mcontactClasses.events.PeopleEvent;
	import mymyoux.utils.Hash;
	import mymyoux.utils.Tampon;
	/**
     * MAdvBinSocket dispatche l'event people_data quand un client envoie une donnée.
     * @eventType mymyoux.io.mserveur.mcontactClasses.events.PeopleEvent
     */
    [Event(name="people_data", type="mymyoux.io.mserveur.mcontactClasses.events.PeopleEvent")]
	/**
     * MAdvBinSocket dispatche l'event disconnect quand le client quitte volontairement un salon.
     * @eventType mymyoux.io.mserveur.events.MRoomEvent
     */
    [Event(name="disconnect", type="mymyoux.io.mserveur.events.MRoomEvent")]
	 /**
     * MAdvBinSocket dispatche l'event data
     * à la réception d'un message dans un salon.
     * @eventType mymyoux.io.mserveur.events.MRoomEvent
     */
    [Event(name="data", type="mymyoux.io.mserveur.events.MRoomEvent")]
	/**
	 * Gère la connexion avec un groupe de MBinServeurs.
	 * @example  <pre class="prettyprint">
	 * package
	 * {
	 * 		import mymyoux.io.mserveur.MRoom;
	 *		import mymyoux.io.mserveur.MAdvBinSocket;
	 *		import mymyoux.io.mserveur.events.MRoomEvent;
	 *		import mx.controls.Alert;
	 * 		public class Test
	 * 		{
	 * 			public function Test(idx:int, pre_idx:String="aventure")
	 * 			{
	 * 				var serveur:MAdvBinSocket=new MAdvBinSocket(idx,pre_idx);
	 * 				var room:MRoom=serveur.goto("DNAROOM");
	 * 				room.addEventListener(MRoomEvent.DATA,onData);
	 * 				//envoi d'un objet
	 * 				var objet:Object=new Object();
	 *				objet.message="coucou";
	 *				room.sendAll(objet);
	 * 			}
	 * 			public function onData(event:MRoomEvent):void
	 * 			{
	 * 				Alert.show(event.value.message);
	 * 			}
	 * 		}
	 * }
	 * </pre>
	 */ 
	public class MAdvBinSocket extends MEventDispatcher
	{
		/**
		 * AMFPHP permettant la liaison avec le serveur PHP.
		 */
		protected var amfphp:AMFPHP;
		/**
		 * Hash contenant tous les MBinServeurs(i.e. MBinarySocket) auxquels le client est connecté.
		 * @see MBinarySocket
		 */
		protected var hSockets:Hash;
		/**
		 * Hash contenant la liste des salons dans lequel le client est rentré.
		 */
		protected var hRooms:Hash;
		/**
		 * Numéro d'identification du client.
		 */
		protected var idx:uint;
		/**
		 * Pré-identifiant : Permet d'utiliser plusieurs applications en même temps sur le même jeu de MBinaryServeurs.
		 * Elimine le risque d'utiliser 2 fois le même idx provenant de 2 appplications différentes.
		 */
		 protected var pre_idx:String;
		/**
		 * Tampon permettant de temporiser l'utilisation des fonctions de MAdvBinSocket tant que celle-ci n'a pas récupéré le password de connexion.
		 */
		protected var tampon:Tampon;
		 /**
		 * @private
		 */
		 protected var _contacts:MContacts;
		 /**
		 * Crée un élément MAdvBinSocket.
		 * @param idx Idx du client.
		 * @param pre_idx Prefixe de l'idx (permet de séparer les différentes applications sur le serveur.
		 * @param source Prefixe des salons.
		 * @param destination Gateway AMFPHP.
		 */
		public function MAdvBinSocket(idx:uint,pre_idx:String="",source:String="MServeur",destination:String="http://www.spaceperuk.com/myamf/gateway.php")
		{
			registerClassAlias("mymyoux.io.mserveur.mapping.PyGram",mymyoux.io.mserveur.mapping.PyGram);
			amfphp=new AMFPHP(source,destination);
			hSockets=new Hash();
			hRooms=new Hash();
			tampon=new Tampon();
			this.pre_idx=pre_idx;
			this.idx=idx;
			_contacts=new MContacts(this.amfphp);
		}

		/**
		 * Permet de rentrer dans un nouveau salon.
		 * Si le salon n'existe pas, le serveur va être questionné sur quel serveur le crée,
		 * sinon l'instance de la MRoom correspondante est renvoyée.
		 * @param room Nom du salon.
		 * @return MRoom correspondante au salon déjà existant ou une nouvelle instance correspondante au nouveau salon.
		 */
		public function goto(room:String):MRoom
		{
			if(!hRooms.containsKey(room))
			{
				amfphp.call("goto",onGoto,room);
				hRooms.put(room,new MRoom(room,idx,pre_idx,contacts));
			}	
			trace("demande:"+room);
			return hRooms.get(room) as MRoom;
		}
		/**
		 * Appelée si une demande goto s'est effectuée sur un salon qui n'existait pas encore pour le client.
		 * @param event Evenement renvoyé par amfphp contenant l'ip et le port du serveur.
		 */
		protected function onGoto(event:AMFPHPEvent):void
		{
			if(event.success)
			{
				try{
					
					var ip:String=event.value[0];
					var port:uint=Number(event.value[1]);
					var room:String=event.value[2];
					trace(room);
					if(!hRooms.containsKey(room))
					{
						throw(new Error("Non correspondance room <-> demande"));
					}
					if(!hSockets.containsKey(ip+":"+port))
					{
						hSockets.put(ip+":"+port,new MBinarySocket(ip,port,idx,pre_idx,amfphp));
						hSockets.get(ip+":"+port).addEventListener(MBinarySocketEvent.BAD_SERVER,onBadServer);
					}
					var mroom:MRoom=hRooms.get(room) as MRoom;
					mroom.addEventListener(MRoomEvent.DISCONNECT,onDisconnectedRoom);
					mroom.addEventListener(PeopleEvent.PEOPLE_DATA,onPeopleData);
					mroom.addEventListener(MRoomEvent.DATA,onRoomData);
					mroom.socket=hSockets.get(ip+":"+port) as MBinarySocket;
				}catch(error:Error)
				{
					trace(error);
					event.success=false;
				}
			}
			if(!event.success)
			{
				//aucun serveur disponible
				try{
				
					setTimeout(amfphp.call,5000,"goto",onGoto,event.value.pop());	
					//Alert.show("Aucun serveur disponible nouvelle tentative dans 30 secondes : \nEvent original :"+event);
				}
				catch(error:Error)
				{
					Alert.show("Erreur fatale du serveur :\n"+error+"\nEvent original :"+event);
				}
			}
			
		}
		/**
		 * Liste de contacts.
		 */
		 public function get contacts():MContacts
		 {
		 	return _contacts;
		 }
		/**
		 * Appelée lorsque qu'un MBinarySocket est lié à un MBinaryServer qui n'existe plus dans la BDD.
		 * Se charge de trouver pour chacun des salons utilisant ce MBinarySocket, un nouveau MBinarySocket.
		 * @param event Evenenemnt envoyé par le MBinarySocket. contient : message.parameters=[ip,port].
		 */
		protected function onBadServer(event:MBinarySocketEvent):void
		{
			var socket:MBinarySocket=event.target as MBinarySocket;
			socket.destroy();
			var keys:Array=hRooms.getKeys();
			hSockets.remove(socket.ip+":"+socket.port);
			for (var p:String in keys)
			{
				var mroom:MRoom=hRooms.get(keys[p]) as MRoom;
				if(socket.equals(mroom.socket))
				{
					amfphp.call("goto",onGoto,keys[p]);
				}
			}
			
			
		}
		/**
		 * Appelée par MRoom quand celle-ci se disconnecte volontairement.
		 */
		 protected function onDisconnectedRoom(event:MRoomEvent):void
		 {
		 			if(hRooms.containsKey(event.room.name))
					{
						hRooms.remove(event.room.name);
						this.dispatchEvent(event.clone());
					}
		 }
		 /**
		 * Appelée par MRoom un client envoie une donnée.
		 */
		 protected function onPeopleData(event:PeopleEvent):void
		 {
		 	this.dispatchEvent(event.clone());
		 }
		 /**
		 * Appelée par MRoom quand celle-ci reçoit une donnée.
		 */
		 protected function onRoomData(event:MRoomEvent):void
		 {
		 	this.dispatchEvent(event.clone());
		 }
		/*
		protected function onError(event:MRoomEvent):void
		{
			var message:PyGram=event.message;
			mx.controls.Alert.show(message.toString());
			if(message.action==PyGram.BAD_IDENTIFICATION)
			{

				/*var socket:MBinarySocket=event.target as MBinarySocket;
				tampon.add(new Fonction(amfphp.call,
			}
		}*/
	}

}
	internal class Fonction
	{
		protected var _func:Function;
		protected var _params:Array;
		public function Fonction(func:Function,params:Array=null)
		{
			_func=func;
			_params=params;
		}
		public function get func():Function
		{
			return _func;
		}
		public function get params():Array
		{
			return _params;
		}
	}